<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  
  <title>【PHP】第三方登录OAuth2.0 | 程序小工  | Just do it!</title>
  <meta name="description" content="遇见更好的自己~" />
  <meta name="keywords" content="" />
  <meta name="HandheldFriendly" content="True" />
  <meta name="apple-mobile-web-app-capable" content="yes">
  <link rel="shortcut icon" href="/">
  <link rel="alternate" href="/atom.xml" title="程序小工  | Just do it!">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <meta name="description" content="对于网站应用程序，涉及到登录和第三方 api 接口时，都会接触到 Token 等概念，而这部分的逻辑原理则是来自于 OAuth 授权协议， 目前的 OAuth2.0 协议的安全性也是被广泛认可，到目前为止尚且没有发生严重的安全事故。学习 OAuth2.0 协议的工作原理，并了解 qq 登录的流程和实现方式。">
<meta name="keywords" content="oauth">
<meta property="og:type" content="article">
<meta property="og:title" content="【PHP】第三方登录OAuth2.0">
<meta property="og:url" content="http://blog.i2arch.tech/2018/07/05/http-ouath/index.html">
<meta property="og:site_name" content="程序小工  | Just do it!">
<meta property="og:description" content="对于网站应用程序，涉及到登录和第三方 api 接口时，都会接触到 Token 等概念，而这部分的逻辑原理则是来自于 OAuth 授权协议， 目前的 OAuth2.0 协议的安全性也是被广泛认可，到目前为止尚且没有发生严重的安全事故。学习 OAuth2.0 协议的工作原理，并了解 qq 登录的流程和实现方式。">
<meta property="og:locale" content="zh-Hans">
<meta property="og:image" content="http://ww1.sinaimg.cn/large/005EgYNMgy1fsvhp6rqkvj30wz0g0wld.jpg">
<meta property="og:image" content="http://ww1.sinaimg.cn/large/005EgYNMgy1fsvhwnt4zqj30ye0fqdnz.jpg">
<meta property="og:updated_time" content="2018-08-14T07:52:44.420Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="【PHP】第三方登录OAuth2.0">
<meta name="twitter:description" content="对于网站应用程序，涉及到登录和第三方 api 接口时，都会接触到 Token 等概念，而这部分的逻辑原理则是来自于 OAuth 授权协议， 目前的 OAuth2.0 协议的安全性也是被广泛认可，到目前为止尚且没有发生严重的安全事故。学习 OAuth2.0 协议的工作原理，并了解 qq 登录的流程和实现方式。">
<meta name="twitter:image" content="http://ww1.sinaimg.cn/large/005EgYNMgy1fsvhp6rqkvj30wz0g0wld.jpg">
    
  <link href="https://fonts.googleapis.com/css?family=Inconsolata|Titillium+Web" rel="stylesheet">
  <link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
  <link href='//cdn.bootcss.com/node-waves/0.7.5/waves.min.css' rel='stylesheet'>
  <link rel="stylesheet" href="/style.css">
  <script>
    function setLoadingBarProgress(num) {
      document.getElementById('loading-bar').style.width=num+"%";
    }
  </script>
</head>

<body>
  <div id="loading-bar-wrapper">
  <div id="loading-bar"></div>
</div>


  <script>setLoadingBarProgress(20)</script>
  <header class="l_header">
	<div class='wrapper'>
		<div class="nav-main container container--flex">
			<a class="logo flat-box" href='/' >
				程序小工  | Just do it!
			</a>
			<div class='menu'>
				<ul class='h-list'>
					
						<li>
							<a class='flat-box nav-home' href='/'>
								<i class='icon icon-list2'></i>
								首页
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-archives' href='/archives'>
								<i class='icon icon-bookmark'></i>
								归档
							</a>
						</li>
					
						<li>
							<a class='flat-box nav-about' href='/about'>
								<i class='icon icon-user'></i>
								关于
							</a>
						</li>
					
				</ul>
				<div class='underline'></div>
			</div>
			
				<div class="m_search">
					<form name="searchform" class="form u-search-form">
						<input type="text" class="input u-search-input" placeholder="Search" />
						<span class="icon icon-search"></span>
					</form>
				</div>
			
			<ul class='switcher h-list'>
				
					<li class='s-search'><a href='javascript:void(0)'><span class="icon icon-search flat-box"></span></a></li>
				
				<li class='s-menu'><a href='javascript:void(0)'><span class="icon icon-menu flat-box"></span></a></li>
			</ul>
		</div>

		<div class='nav-sub container container--flex'>
			<a class="logo" class="flat-box" href='javascript:void(0)'>
				Word of Forks
			</a>

			<ul class='switcher h-list'>
				<li class='s-comment'><a href='javascript:void(0)'><span class="icon icon-chat_bubble_outline flat-box"></span></a></li>
				<li class='s-top'><a href='javascript:void(0)'><span class="icon icon-arrow_upward flat-box"></span></a></li>
				<li class='s-toc'><a href='javascript:void(0)'><span class="icon icon-format_list_numbered flat-box"></span></a></li>
			</ul>
		</div>
	</div>
</header>
<aside class="menu-phone">
	<nav>
		
			<a href="/" class="nav-home nav">
				首页
			</a>
		
			<a href="/archives" class="nav-archives nav">
				归档
			</a>
		
			<a href="/about" class="nav-about nav">
				关于
			</a>
		
	</nav>
</aside>

    <script>setLoadingBarProgress(40);</script>
  <div class="l_body">
    <div class='container clearfix'>
      <!-- 左侧内容 -->
      <div class='l_main'>
        <article id="post-http-ouath"
  class="post white-box article-type-post"
  itemscope itemprop="blogPost">
	<section class='meta'>

	<h2 class="title">
  	<a href="/2018/07/05/http-ouath/">
    	【PHP】第三方登录OAuth2.0
    </a>
	</h2>

	<div class="pagenav">
		<time>
			2018-07-05
		</time>

		
    
    <div class='cats'>
        <i class="icon icon-bookmark"></i>
        <a href="/categories/PHP/">PHP</a>
    </div>


		
			<div class="pagetags">
				<i class="icon icon-price-tag" style="margin-right: 2px;"></i>
				
					<a href="/tags/oauth/" style="margin-left: 3px;">
						oauth
					</a>
				
			</div>
			
	</div>
	</section>

	
	<section class="toc-wrapper"><ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#oauth20-协议工作原理"><span class="toc-text"> OAuth2.0 协议工作原理</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#步骤一请求-oauth-登录页"><span class="toc-text"> 步骤一：请求 OAuth 登录页</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#步骤二用户使用第三方账号登录并授权"><span class="toc-text"> 步骤二：用户使用第三方账号登录并授权</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#步骤三返回登录结果"><span class="toc-text"> 步骤三：返回登录结果</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#accesstoken-和-refreshtoken-数据传输原理"><span class="toc-text"> AccessToken 和 RefreshToken 数据传输原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#accesstoken-和-refreshtoken-生命周期解析"><span class="toc-text"> AccessToken 和 RefreshToken 生命周期解析</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#qq-登录"><span class="toc-text"> QQ 登录</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#1接入-qq-开放平台的前置条件"><span class="toc-text"> 1.接入 QQ 开放平台的前置条件</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#2申请-appid-和-appkey"><span class="toc-text"> 2.申请 AppID 和 AppKey</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#3添加测试回调地址"><span class="toc-text"> 3.添加测试回调地址</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#4引入官方-sdk"><span class="toc-text"> 4.引入官方 SDK</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#5sdk-参数配置"><span class="toc-text"> 5.SDK 参数配置</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#6sdk-解读"><span class="toc-text"> 6.SDK 解读</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#7sdk-优化"><span class="toc-text"> 7.SDK 优化</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#8整合-sdk-到-web-项目中请求访问-qq-登录页面"><span class="toc-text"> 8.整合 SDK 到 Web 项目中–请求访问 QQ 登录页面</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9整合-sdk-到-web-项目中获取-code-和-accesstoken"><span class="toc-text"> 9.整合 SDK 到 Web 项目中–获取 code 和 AccessToken</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#9整合-sdk-到-web-项目中获取-openid"><span class="toc-text"> 9.整合 SDK 到 Web 项目中–获取 openID</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#10api-调用示例"><span class="toc-text"> 10.API 调用示例</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#调用get_user_info接口获取用户信息"><span class="toc-text"> 调用get_user_info接口，获取用户信息</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#平台政策与注意事项"><span class="toc-text"> 平台政策与注意事项</span></a></li></ol></li></ol></section>
	

	<section class="article typo">
		<div class="article-entry" itemprop="articleBody">
				<span class="zhaiyao">[摘要]</span>
			<p>对于网站应用程序，涉及到登录和第三方 api 接口时，都会接触到 Token 等概念，而这部分的逻辑原理则是来自于 OAuth 授权协议，<br>
目前的 OAuth2.0 协议的安全性也是被广泛认可，到目前为止尚且没有发生严重的安全事故。学习 OAuth2.0 协议的工作原理，并了解 qq 登录的流程和实现方式。</p>
<a id="more"></a>
<h2 id="oauth20-协议工作原理"><a class="markdownIt-Anchor" href="#oauth20-协议工作原理"></a> OAuth2.0 协议工作原理</h2>
<p><img src="http://ww1.sinaimg.cn/large/005EgYNMgy1fsvhp6rqkvj30wz0g0wld.jpg" alt=""></p>
<h3 id="步骤一请求-oauth-登录页"><a class="markdownIt-Anchor" href="#步骤一请求-oauth-登录页"></a> 步骤一：请求 OAuth 登录页</h3>
<p>Request Token URL - 未授权的令牌请求服务地址<a href="client_id,redirect_uri">慕课网请求 QQ 登录页面时使用的带有特定参数的 URL</a></p>
<h3 id="步骤二用户使用第三方账号登录并授权"><a class="markdownIt-Anchor" href="#步骤二用户使用第三方账号登录并授权"></a> 步骤二：用户使用第三方账号登录并授权</h3>
<p>身份认证通过后，会跳转到第一步的 redirect_uri，并携带 code 参数</p>
<h3 id="步骤三返回登录结果"><a class="markdownIt-Anchor" href="#步骤三返回登录结果"></a> 步骤三：返回登录结果</h3>
<p>User Authorization URL - 用户授权的令牌请求服务地址<a href="client_id,client_secret,code">用户 QQ 登录授权之后需要请求的一个带有特定参数的 URL</a><br>
code 有生命周期且只可使用一次的字符串</p>
<p>AccessToken - 用户通过第三方应用访问 OAuth 接口的令牌[通过慕课网把自己喜欢的课程分享到 QQ 空间]<br>
Refresh Token</p>
<h3 id="accesstoken-和-refreshtoken-数据传输原理"><a class="markdownIt-Anchor" href="#accesstoken-和-refreshtoken-数据传输原理"></a> AccessToken 和 RefreshToken 数据传输原理</h3>
<p>[imooc]带有 AccessToken 参数的特定 URL=&gt;[post]=&gt;[QQ]open Authorization API=&gt;[xml/json]=&gt;[imooc]带有 AccessToken 参数的特定 URL</p>
<h3 id="accesstoken-和-refreshtoken-生命周期解析"><a class="markdownIt-Anchor" href="#accesstoken-和-refreshtoken-生命周期解析"></a> AccessToken 和 RefreshToken 生命周期解析</h3>
<p>AccessToken - 具有较长生命周期(10 天半个月甚至更长)<br>
User Authorization URL 中指定参数 RefreshToken 进行重新获取 AccessToken</p>
<h2 id="qq-登录"><a class="markdownIt-Anchor" href="#qq-登录"></a> QQ 登录</h2>
<h3 id="1接入-qq-开放平台的前置条件"><a class="markdownIt-Anchor" href="#1接入-qq-开放平台的前置条件"></a> 1.接入 QQ 开放平台的前置条件</h3>
<ul>
<li>
<p>qq 号</p>
</li>
<li>
<p>公网可访问的 web 服务器</p>
</li>
<li>
<p>关于域名备案</p>
</li>
</ul>
<p><img src="http://ww1.sinaimg.cn/large/005EgYNMgy1fsvhwnt4zqj30ye0fqdnz.jpg" alt=""></p>
<p>(腾讯的用于域名验证，拿到 appid 等信息)</p>
<ul>
<li>关于服务器运行环境</li>
</ul>
<h3 id="2申请-appid-和-appkey"><a class="markdownIt-Anchor" href="#2申请-appid-和-appkey"></a> 2.申请 AppID 和 AppKey</h3>
<p><a href="https://connect.qq.com" target="_blank" rel="noopener">QQ 互联</a></p>
<p>网站地址[需要在该页面下的 index.html 文件中嵌入一行代码，然后进行验证]<br>
回调地址[可以填写多个，英文半角分号;间隔，加 http(s)😕/头]</p>
<p>[每次修改配置后都需要重新验证网站地址]</p>
<h3 id="3添加测试回调地址"><a class="markdownIt-Anchor" href="#3添加测试回调地址"></a> 3.添加测试回调地址</h3>
<p>eg. <a href="http://test.open.mypro.com/callback.php" target="_blank" rel="noopener">http://test.open.mypro.com/callback.php</a></p>
<h3 id="4引入官方-sdk"><a class="markdownIt-Anchor" href="#4引入官方-sdk"></a> 4.引入官方 SDK</h3>
<p><a href="http://wiki.connect.qq.com/sdk%E4%B8%8B%E8%BD%BD" target="_blank" rel="noopener">下载</a></p>
<h3 id="5sdk-参数配置"><a class="markdownIt-Anchor" href="#5sdk-参数配置"></a> 5.SDK 参数配置</h3>
<ul>
<li>
<p>appid</p>
</li>
<li>
<p>appkey</p>
</li>
<li>
<p>callback</p>
</li>
<li>
<p>请求授权列表</p>
<pre><code>  get_user_info add_share list_album add_album ...
</code></pre>
<p>[请求的权限会在授权登录页面显示需要请求的信息列表]</p>
</li>
<li>
<p>是否开通调试</p>
</li>
</ul>
<h3 id="6sdk-解读"><a class="markdownIt-Anchor" href="#6sdk-解读"></a> 6.SDK 解读</h3>
<ul>
<li>
<p>文档资料 -&gt; oauth 开发指引 -&gt; 开发功率_server-side</p>
</li>
<li>
<p>Server-side or Client-side</p>
</li>
</ul>
<p>核心类和重要方法(Connectx.x/class/*.class.php)</p>
<ul>
<li>
<p>Recorder.class.php[配置读写与 SESSION 存取]</p>
<ul>
<li>__construct()
<ul>
<li>读入配置文件 json 串：$incFileContents = file(ROOT.“comm/inc.php”)</li>
<li>$incFileContents = $incFileContents[1];</li>
<li>解析成 php 对象：<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mo>−</mo><mo>&gt;</mo><mi>i</mi><mi>n</mi><mi>c</mi><mo>=</mo><mi>j</mi><mi>s</mi><mi>o</mi><msub><mi>n</mi><mi>d</mi></msub><mi>e</mi><mi>c</mi><mi>o</mi><mi>d</mi><mi>e</mi><mo>(</mo></mrow><annotation encoding="application/x-tex">this-&gt;inc = json_decode(</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.75em;"></span><span class="strut bottom" style="height:1em;vertical-align:-0.25em;"></span><span class="base"><span class="mord mathit">t</span><span class="mord mathit">h</span><span class="mord mathit">i</span><span class="mord mathit">s</span><span class="mord">−</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">i</span><span class="mord mathit">n</span><span class="mord mathit">c</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit" style="margin-right:0.05724em;">j</span><span class="mord mathit">s</span><span class="mord mathit">o</span><span class="mord"><span class="mord mathit">n</span><span class="msupsub"><span class="vlist-t vlist-t2"><span class="vlist-r"><span class="vlist" style="height:0.33610799999999996em;"><span style="top:-2.5500000000000003em;margin-left:0em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mathit mtight">d</span></span></span></span><span class="vlist-s">​</span></span><span class="vlist-r"><span class="vlist" style="height:0.15em;"></span></span></span></span></span><span class="mord mathit">e</span><span class="mord mathit">c</span><span class="mord mathit">o</span><span class="mord mathit">d</span><span class="mord mathit">e</span><span class="mopen">(</span></span></span></span>incFileContents);</li>
</ul>
</li>
<li>readInc($name)
<ul>
<li>return <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mo>−</mo><mo>&gt;</mo><mi>i</mi><mi>n</mi><mi>c</mi><mo>−</mo><mo>&gt;</mo></mrow><annotation encoding="application/x-tex">this-&gt;inc-&gt;</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.69444em;"></span><span class="strut bottom" style="height:0.77777em;vertical-align:-0.08333em;"></span><span class="base"><span class="mord mathit">t</span><span class="mord mathit">h</span><span class="mord mathit">i</span><span class="mord mathit">s</span><span class="mord">−</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit">i</span><span class="mord mathit">n</span><span class="mord mathit">c</span><span class="mord">−</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span></span></span></span>name;</li>
<li>//-&gt;readInc(‘appid’) 既读取配置文件的 appid</li>
</ul>
</li>
</ul>
</li>
<li>
<p>URL.class.php[基于 CURL 库的 get 与 post 请求]</p>
<ul>
<li>combineURL($baseURL, $keysArr)
<ul>
<li>拼接：$combined = $baseURL.&quot;?&quot;;</li>
<li>拼接参数: foreach($keysArr as <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>k</mi><mi>e</mi><mi>y</mi><mo>=</mo><mo>&gt;</mo></mrow><annotation encoding="application/x-tex">key=&gt;</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.69444em;"></span><span class="strut bottom" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="base"><span class="mord mathit" style="margin-right:0.03148em;">k</span><span class="mord mathit">e</span><span class="mord mathit" style="margin-right:0.03588em;">y</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mrel">&gt;</span></span></span></span>val) <span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>v</mi><mi>a</mi><mi>l</mi><mi>u</mi><mi>e</mi><mi>A</mi><mi>r</mi><mi>r</mi><mo>[</mo><mo>]</mo><mo>=</mo><mi mathvariant="normal">&quot;</mi></mrow><annotation encoding="application/x-tex">valueArr[] = &quot;</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.75em;"></span><span class="strut bottom" style="height:1em;vertical-align:-0.25em;"></span><span class="base"><span class="mord mathit" style="margin-right:0.03588em;">v</span><span class="mord mathit">a</span><span class="mord mathit" style="margin-right:0.01968em;">l</span><span class="mord mathit">u</span><span class="mord mathit">e</span><span class="mord mathit">A</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mopen">[</span><span class="mclose">]</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">=</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord">&quot;</span></span></span></span>key=$val&quot;;</li>
<li>使用&amp;拼接参数键值对：$keyStr = implode(&quot;&amp;&quot;, $valueArr);</li>
<li>生成形如http://xxx.com?a=b&amp;c=d…的链接</li>
</ul>
</li>
<li>get(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>u</mi><mi>r</mi><mi>l</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">url,</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.69444em;"></span><span class="strut bottom" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="base"><span class="mord mathit">u</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit" style="margin-right:0.01968em;">l</span><span class="mpunct">,</span></span></span></span>keysArr) 发送 get 请求</li>
<li>post(<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>u</mi><mi>r</mi><mi>l</mi><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">url,</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.69444em;"></span><span class="strut bottom" style="height:0.8888799999999999em;vertical-align:-0.19444em;"></span><span class="base"><span class="mord mathit">u</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit" style="margin-right:0.01968em;">l</span><span class="mpunct">,</span></span></span></span>keysArr,$flag = 0) 发送 post 请求</li>
</ul>
</li>
<li>
<p>Oauth.class.php[Oauth 相关 URL 动态拼接与 token 操作]</p>
<ul>
<li>qq_login() 拼接 QQ 登录页面 URL
<ul>
<li>读取 appid: $appid = $this-&gt;recoder-&gt;readInc(“appid”);</li>
<li>读取回调地: $callback = $this-&gt;recoder-&gt;readInc(“callback”);</li>
<li>读取授权列表：$this-&gt;recorder-&gt;readInc(“scope”);</li>
<li>生成唯一随机串防 CSRF 攻击</li>
<li>原样返回参数：md5(uniqid(rand(), TRUE));</li>
<li>state 写入 session 中：<span class="katex"><span class="katex-mathml"><math><semantics><mrow><mi>t</mi><mi>h</mi><mi>i</mi><mi>s</mi><mo>−</mo><mo>&gt;</mo><mi>r</mi><mi>e</mi><mi>c</mi><mi>o</mi><mi>r</mi><mi>d</mi><mi>e</mi><mi>r</mi><mo>−</mo><mo>&gt;</mo><mi>w</mi><mi>r</mi><mi>i</mi><mi>t</mi><mi>e</mi><msup><mo>(</mo><mo mathvariant="normal">′</mo></msup><mi>s</mi><mi>t</mi><mi>a</mi><mi>t</mi><msup><mi>e</mi><mo mathvariant="normal">′</mo></msup><mo separator="true">,</mo></mrow><annotation encoding="application/x-tex">this-&gt;recorder-&gt;write(&#x27;state&#x27;,</annotation></semantics></math></span><span class="katex-html" aria-hidden="true"><span class="strut" style="height:0.751892em;"></span><span class="strut bottom" style="height:1.001892em;vertical-align:-0.25em;"></span><span class="base"><span class="mord mathit">t</span><span class="mord mathit">h</span><span class="mord mathit">i</span><span class="mord mathit">s</span><span class="mord">−</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit">e</span><span class="mord mathit">c</span><span class="mord mathit">o</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit">d</span><span class="mord mathit">e</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord">−</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mrel">&gt;</span><span class="mord rule" style="margin-right:0.2777777777777778em;"></span><span class="mord mathit" style="margin-right:0.02691em;">w</span><span class="mord mathit" style="margin-right:0.02778em;">r</span><span class="mord mathit">i</span><span class="mord mathit">t</span><span class="mord mathit">e</span><span class="mopen"><span class="mopen">(</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mord mathit">s</span><span class="mord mathit">t</span><span class="mord mathit">a</span><span class="mord mathit">t</span><span class="mord"><span class="mord mathit">e</span><span class="msupsub"><span class="vlist-t"><span class="vlist-r"><span class="vlist" style="height:0.751892em;"><span style="top:-3.063em;margin-right:0.05em;"><span class="pstrut" style="height:2.7em;"></span><span class="sizing reset-size6 size3 mtight"><span class="mord mtight"><span class="mord mtight">′</span></span></span></span></span></span></span></span></span><span class="mpunct">,</span></span></span></span>state);<br>
=》拼接之后得到https://graph.qq.com/oauth2.0/authorize?response_type=code&amp;client_id=[APPID]&amp;redirect_uri=[REDIRECT_URI]&amp;scope=[SCOPE]&amp;state=[STATE]</li>
<li>跳转到生成的 url: header(“Location:$login_url”);</li>
</ul>
</li>
<li>qq_callback() QQ 登录完成后的回调处理</li>
</ul>
</li>
</ul>
<h3 id="7sdk-优化"><a class="markdownIt-Anchor" href="#7sdk-优化"></a> 7.SDK 优化</h3>
<ul>
<li>SDK 太老，很久无人维护
<ul>
<li>调整文件及目录结构</li>
</ul>
</li>
<li>SDK 中的常量名太常见，可能和现有项目冲突
<ul>
<li>批量替换 SDK 中常量名称为不常见名称</li>
</ul>
</li>
</ul>
<h3 id="8整合-sdk-到-web-项目中请求访问-qq-登录页面"><a class="markdownIt-Anchor" href="#8整合-sdk-到-web-项目中请求访问-qq-登录页面"></a> 8.整合 SDK 到 Web 项目中–请求访问 QQ 登录页面</h3>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$oauth = <span class="keyword">new</span> Oauth();</span><br><span class="line">$oauth-&gt;qq_login();</span><br></pre></td></tr></table></figure>
<h3 id="9整合-sdk-到-web-项目中获取-code-和-accesstoken"><a class="markdownIt-Anchor" href="#9整合-sdk-到-web-项目中获取-code-和-accesstoken"></a> 9.整合 SDK 到 Web 项目中–获取 code 和 AccessToken</h3>
<ul>
<li>
<p>回调地址：<a href="http://test.open.mypro.com/callback.php" target="_blank" rel="noopener">http://test.open.mypro.com/callback.php</a></p>
</li>
<li>
<p>拿到返回的<code>code</code>，并请求 AccessToken</p>
</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$oauth = <span class="keyword">new</span> Oauth();</span><br><span class="line">$accessToken = $oauth-&gt;qq_callback();</span><br></pre></td></tr></table></figure>
<h3 id="9整合-sdk-到-web-项目中获取-openid"><a class="markdownIt-Anchor" href="#9整合-sdk-到-web-项目中获取-openid"></a> 9.整合 SDK 到 Web 项目中–获取 openID</h3>
<p>(1) 关于 openId</p>
<ul>
<li>QQ 用户在第三方站点的唯一标识</li>
<li>同一个 QQ 用户在不同站点使用 QQ 登录 openId 始终一样</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$openid = $oauth-&gt;get_openid();</span><br></pre></td></tr></table></figure>
<p>(2)存储<code>accesstoken</code>和<code>openid</code>到<code>cookie</code>中</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 有效期时长可以读取session中的相应信息的有效期 [手动设置时需要将该时长小于实际有效期]</span></span><br><span class="line">setcookie(<span class="string">'qq_accesstoken'</span>, $accesstoken, time()+<span class="number">86400</span>);</span><br><span class="line">setcookie(<span class="string">'qq_openid'</span>, $openid, time()+<span class="number">86400</span>);</span><br></pre></td></tr></table></figure>
<h3 id="10api-调用示例"><a class="markdownIt-Anchor" href="#10api-调用示例"></a> 10.API 调用示例</h3>
<h4 id="调用get_user_info接口获取用户信息"><a class="markdownIt-Anchor" href="#调用get_user_info接口获取用户信息"></a> 调用<code>get_user_info</code>接口，获取用户信息</h4>
<p>(1)回调成功后，跳转到<code>index.php</code>文件</p>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">header(<span class="string">'Location: index.php'</span>);</span><br></pre></td></tr></table></figure>
<p>(2)判断当前登录状态[通过 cookie]</p>
<ul>
<li>
<p>未登录</p>
<ul>
<li>进行登录 [获取 AccessToken，获取 openid]</li>
</ul>
</li>
<li>
<p>已经登录</p>
<ul>
<li>调用接口，获取信息</li>
</ul>
</li>
</ul>
<figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$qc = <span class="keyword">new</span> QC($_COOKIE[<span class="string">'qq_accesstoken'</span>], $_COOKIE[<span class="string">'qq_openid'</span>]);</span><br><span class="line">$userInfo = $qc-&gt;get_user_info();</span><br></pre></td></tr></table></figure>
<h3 id="平台政策与注意事项"><a class="markdownIt-Anchor" href="#平台政策与注意事项"></a> 平台政策与注意事项</h3>
<ul>
<li>
<p>APPID 申请之后 3 个月未申请上线将被回收</p>
</li>
<li>
<p>申请上线需要使用官网提供的 QQ 登录按钮素材</p>
</li>
<li>
<p>遵守国家法律法规, 拒绝黄赌毒</p>
</li>
</ul>

		</div>

		
			<div class="art-item-footer">
					
						<span class="art-item-left"><i class="icon icon-chevron-thin-left"></i> <a href="/2018/07/05/php-practice-wx-tp5-2/" rel="prev" title="微信小程序结合ThinkPHP5实战项目（二）-- 接口编写">
							微信小程序结合ThinkPHP5实战项目（二）-- 接口编写
						</a>上一篇文章</span>
					
					
						<span class="art-item-right">下一篇文章：<a href="/2018/07/01/php-practice-wx-tp5-1/" rel="next"  title="微信小程序结合ThinkPHP5实战项目（一）-- 数据库访问与ORM">
							微信小程序结合ThinkPHP5实战项目（一）-- 数据库访问与ORM
						</a><i class="icon icon-chevron-thin-right"></i></span>
					
			</div>
		

		
			

    <div class="recommended_posts">
        <h3>推荐文章</h3>
        <ul>
            
                <li><a href="http://blog.i2arch.tech/2018/07/06/linux-crontab/">【Linux】系统学习Crontab定时任务</a></li>
            
                <li><a href="http://blog.i2arch.tech/2018/07/05/php-practice-wx-tp5-2/">微信小程序结合ThinkPHP5实战项目（二）-- 接口编写</a></li>
            
                <li><a href="http://blog.i2arch.tech/2018/07/01/php-practice-wx-tp5-1/">微信小程序结合ThinkPHP5实战项目（一）-- 数据库访问与ORM</a></li>
            
                <li><a href="http://blog.i2arch.tech/2018/06/29/windows-tool/">【转】windows系统下的高效工具</a></li>
            
        </ul>
    </div>


		
	</section>

	

</article>
<script>
	window.subData = {
		title: '【PHP】第三方登录OAuth2.0',
		tools: true
	}
</script>

      </div>

      <!-- 右侧边栏组件 -->
      <aside class='l_side'>
        
      </aside>

      <div class="gotop">
          
    <a href='javascript:void(0)'>
        <i class="icon icon-arrow_upward flat-box">
        </i>
    </a>

      </div>
      <script>setLoadingBarProgress(60);</script>
    </div>
  </div>
  <footer id="footer" class="clearfix">

	<div class="social-wrapper">
  	
      
        <a href="https://github.com/zqunor" class="social github"
          target="_blank" rel="external">
          <span class="icon icon-github"></span>
        </a>
      
        <a href="https://www.cnblogs.com/zqunor" class="social zhihu"
          target="_blank" rel="external">
          <span class="icon icon-zhihu"></span>
        </a>
      
        <a href="/atom.xml" class="social rss"
          target="_blank" rel="external">
          <span class="icon icon-rss"></span>
        </a>
      
    
  </div>
  <div>Theme <a href='https://github.com/stkevintan/hexo-theme-material-flow' class="codename">MaterialFlow</a>  Powered by<a href="https://blog.i2arch.tech/" target="_blank">程序小工</a>.</div>

</footer>

  <script>setLoadingBarProgress(80);</script>
  

    <script src="//apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
    <script src='//cdn.bootcss.com/node-waves/0.7.5/waves.min.js'></script>
    <script src="//cdn.bootcss.com/scrollReveal.js/3.3.2/scrollreveal.min.js"></script>
    <script src="/js/jquery.fitvids.js"></script>
      <script>
        var GOOGLE_CUSTOM_SEARCH_API_KEY = "";
        var GOOGLE_CUSTOM_SEARCH_ENGINE_ID = "";
        var ALGOLIA_API_KEY = "";
        var ALGOLIA_APP_ID = "";
        var ALGOLIA_INDEX_NAME = "";
        var AZURE_SERVICE_NAME = "";
        var AZURE_INDEX_NAME = "";
        var AZURE_QUERY_KEY = "";
        var BAIDU_API_ID = "fca5b83cf69f752ddc96a61ef64a3659";
        var SEARCH_SERVICE = "hexo";
        var ROOT = "/" || "/";
        if (!ROOT.endsWith('/')) ROOT += '/';
      </script>
      <script src="/js/search.js"></script>
        <script src="/js/app.js"></script>
  <script>setLoadingBarProgress(100);</script>
</body>
</html>
